React SuspenseãããŒãã£ã³ã°ç¶æ 管çãšãšã©ãŒãã³ããªã³ã°ãç°¡çŽ åãã倿§ãªã°ããŒãã«ç°å¢ã§ã®ãŠãŒã¶ãŒäœéšãåäžãããæ¹æ³ã解説ããŸãã
React Suspense: ããŒãã£ã³ã°ç¶æ ãšãšã©ãŒããŠã³ããªãŒãã°ããŒãã«ã«ç®¡çãã
Webéçºã®ãã€ãããã¯ãªäžçã§ã¯ããŠãŒã¶ãŒã®å Žæãããã€ã¹ããããã¯ãŒã¯ç¶æ³ã«é¢ããããã¹ã ãŒãºã§é åçãªãŠãŒã¶ãŒäœéšãæäŸããããšãæãéèŠã§ããReactãšã³ã·ã¹ãã ã®åŒ·åãªæ©èœã§ããReact Suspenseã¯ãããŒãã£ã³ã°ç¶æ ã管çãããšã©ãŒããšã¬ã¬ã³ãã«åŠçããããã®å ç¢ãªã¡ã«ããºã ãæäŸããŸãããã®ã¬ã€ãã§ã¯ãReact Suspenseã®ã³ã¢ã³ã³ã»ãããæãäžããã°ããŒãã«ã«ã¢ã¯ã»ã¹å¯èœã§ããã©ãŒãã³ã¹ã®é«ãã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããã®å®è·µçãªæŽå¯ãšäŸã玹ä»ããŸãã
Suspenseã®å¿ èŠæ§ãçè§£ãã
çŸä»£ã®Webã¢ããªã±ãŒã·ã§ã³ã¯ãAPIããã®ããŒã¿ãã§ããã倧ããªç»åãåç»ã®èªã¿èŸŒã¿ãããã©ãŒãã³ã¹æé©åã®ããã®ã³ãŒãåå²ãªã©ãéåæåŠçã«é »ç¹ã«äŸåããŠããŸãããããã®åŠçã¯é å»¶ãåŒãèµ·ããå¯èœæ§ããããäžé©åãªããŒãã£ã³ã°äœéšã¯ãŠãŒã¶ãŒãèç«ãããé¢è±ã«ã€ãªããå¯èœæ§ããããŸããåŸæ¥ãéçºè ã¯ãããã®ã·ããªãªã管çããããã«ã次ã®ãããªæ§ã ãªãã¯ããã¯ãæ¡çšããŠããŸããïŒ
- ããŒãã£ã³ã°ã¹ãããŒã®è¡šç€ºã
- ãã¬ãŒã¹ãã«ããŒã³ã³ãã³ãã®è¡šç€ºã
- åã³ã³ããŒãã³ãå ã§ã®ããŒãã£ã³ã°ç¶æ ãšãšã©ãŒç¶æ ã®æå管çã
ãããã®ã¢ãããŒãã¯å¹æçã§ããäžæ¹ãã³ãŒããè€éã§åé·ã«ãªããã¡ã§ãã¢ããªã±ãŒã·ã§ã³ã®ä¿å®ãã¹ã±ãŒã«ãå°é£ã«ãªãããšããããããŸããReact Suspenseã¯ãããŒãã£ã³ã°ç¶æ ãšãšã©ãŒç¶æ ã宣èšçã«åŠçããæ¹æ³ãæäŸããããšã§ãã®ããã»ã¹ãåçåããéçºè äœéšãšãšã³ããŠãŒã¶ãŒäœéšã®äž¡æ¹ãå€§å¹ ã«åäžãããŸãã
React Suspenseãšã¯äœãïŒ
React Suspenseã¯ãç¹å®ã®æ¡ä»¶ãæºãããããŸã§ã³ã³ããŒãã³ãã®ã¬ã³ããªã³ã°ããäžæïŒsuspendïŒãã§ããReactã®çµã¿èŸŒã¿æ©èœã§ãããã®æ¡ä»¶ã¯éåžžãããŒã¿ãã§ãããªã©ã®éåæåŠçã®å®äºã§ãããã®ãäžæãç¶æ ã®éãReactã¯ããŒãã£ã³ã°ã¹ãããŒããã¬ãŒã¹ãã«ããŒã³ã³ããŒãã³ããªã©ã®ãã©ãŒã«ããã¯UIã衚瀺ã§ããŸããéåæåŠçãå®äºãããšãReactã¯ååŸããããŒã¿ã§ã³ã³ããŒãã³ãã®ã¬ã³ããªã³ã°ãåéããŸãã
Suspenseã¯äž»ã«ãWebã¢ããªã±ãŒã·ã§ã³éçºã®2ã€ã®éèŠãªåŽé¢ã«å¯Ÿå¿ããŸãïŒ
- ããŒãã£ã³ã°ç¶æ ã®èª¿æŽïŒ Suspenseã¯ãããŒãã£ã³ã°ã€ã³ãžã±ãŒã¿ãŒããã¬ãŒã¹ãã«ããŒã®ç®¡çãç°¡çŽ åããŸããéçºè ã¯ãã¯ãåã ã®ã³ã³ããŒãã³ãã®ããŒãã£ã³ã°ç¶æ ãæåã§è¿œè·¡ããå¿ èŠã¯ãããŸããã代ããã«ãSuspenseã¯ã¢ããªã±ãŒã·ã§ã³å šäœã§ãããã®ç¶æ ãåŠçããããã®äžå€®éæš©çãªã¡ã«ããºã ãæäŸããŸãã
- ãšã©ãŒããŠã³ããªãŒã®ç®¡çïŒ Suspenseã¯ãšã©ãŒããŠã³ããªãŒãšã·ãŒã ã¬ã¹ã«çµ±åãããŸãããšã©ãŒããŠã³ããªãŒã¯ãåã³ã³ããŒãã³ãããªãŒå ã®ã©ããã§çºçããJavaScriptãšã©ãŒããã£ãããããããã®ãšã©ãŒããã°ã«èšé²ããã¢ããªã±ãŒã·ã§ã³å šäœãã¯ã©ãã·ã¥ããã代ããã«ãã©ãŒã«ããã¯UIã衚瀺ããReactã³ã³ããŒãã³ãã§ããããã«ãããåäžã®ãšã©ãŒããŠãŒã¶ãŒã€ã³ã¿ãŒãã§ãŒã¹å šäœã忢ãããã®ãé²ããŸãã
ã³ã¢ã³ã³ã»ããïŒéåæåŠçãšãã©ãŒã«ããã¯
React Suspenseã®åºç€ã¯ãéåæåŠçãæ±ãèœåã«ãããŸããSuspenseã䜿çšããã«ã¯ãéåæåŠçããäžæå¯èœïŒsuspensibleïŒãã§ããå¿ èŠããããŸããããã«ã¯éåžžã`react-cache`ã®ãããªã©ã€ãã©ãªïŒçŸåšã¯ããéæšå¥šã§ããïŒããReactã®Suspenseã¡ã«ããºã ãšçµ±åããã«ã¹ã¿ã å®è£ ã䜿çšããããšãå«ãŸããŸãããããã®ã¢ãããŒãã«ãããã³ã³ããŒãã³ãã¯äœããåŸ ã£ãŠããããšãéç¥ãããã©ãŒã«ããã¯UIã®è¡šç€ºãããªã¬ãŒããããšãã§ããŸãã
ãã©ãŒã«ããã¯ã¯éåžžã«éèŠã§ãããããã¯ãã³ã³ããŒãã³ããäžæãããŠããéã«è¡šç€ºãããèŠèŠçãªè¡šçŸã§ãããããã®ãã©ãŒã«ããã¯ã¯ãåçŽãªããŒãã£ã³ã°ã¹ãããŒãã¹ã±ã«ãã³UIããŸãã¯ããæŽç·Žããããã¬ãŒã¹ãã«ããŒã«ããããšãã§ããŸãããã©ãŒã«ããã¯ã®éžæã¯ãäœæããããŠãŒã¶ãŒäœéšã«ãã£ãŠç°ãªããŸããçæ³çãªãã©ãŒã«ããã¯ã¯ãæçã§éªéã«ãªããããŠãŒã¶ãŒã«ã¢ããªã±ãŒã·ã§ã³ãå£ããŠãããšæããããªããã®ã§ãã
äŸïŒSuspenseã䜿ã£ãããŒã¿ãã§ãã
SuspenseãããŒã¿ãã§ãããšå ±ã«äœ¿çšããæ¹æ³ã瀺ãç°¡åãªäŸãèŠãŠã¿ãŸããããããã¯ã`fetchData`ãšãã颿°ã䜿çšããä»®ã®APIåŒã³åºããæ³å®ããŠããŸãïŒå®è£ ã®è©³çްã¯ç°¡æœãã®ããã«çç¥ãããŠããŸãïŒã
import React, { Suspense, useState, useEffect } from 'react';
// Assume this function fetches data and 'suspends' the component
async function fetchData(resource) {
// Simulate API call delay
await new Promise(resolve => setTimeout(resolve, 1000));
// Replace with actual API call, handling potential errors.
// This is a simplified example; consider error handling here.
const response = await fetch(`https://api.example.com/${resource}`);
const data = await response.json();
return data;
}
function ProfileDetails({ resource }) {
const [data, setData] = useState(null);
useEffect(() => {
async function loadData() {
const result = await fetchData(resource);
setData(result);
}
loadData();
}, [resource]);
if (!data) {
throw fetchData(resource); // Signal Suspense
}
return (
{data.name}
Email: {data.email}
);
}
function Profile() {
return (
Loading profile... My App
ãã®äŸã§ã¯ïŒ
- `ProfileDetails`ã³ã³ããŒãã³ããããŒã¿ããã§ããããŸãã
- `fetchData`ãåŒã³åºããããšãAPIåŒã³åºããã·ãã¥ã¬ãŒãããŸãã
- ããŒã¿ããŸã ããŒããããŠããªãå Žåã`ProfileDetails`ã¯`fetchData`ããè¿ãããPromiseã*ã¹ããŒ*ããŸãããããReactã«ã³ã³ããŒãã³ããäžæããããéç¥ããéèŠãªéšåã§ããReactã¯ããããã£ããããè¿ãã®`Suspense`ããŠã³ããªãŒãæ¢ããŸãã
- `
`ã³ã³ããŒãã³ãã¯ã`ProfileDetails`ãããŒã¿ãåŸ ã£ãŠããéã«è¡šç€ºããããã©ãŒã«ããã¯ãæäŸããŸãã - ããŒã¿ããã§ããããããšã`ProfileDetails`ã¯ãããã£ãŒã«æ å ±ãã¬ã³ããªã³ã°ããŸãã
ãšã©ãŒããŠã³ããªãŒïŒã¯ã©ãã·ã¥ããã®ä¿è·
ãšã©ãŒããŠã³ããªãŒã¯ãåã³ã³ããŒãã³ãããªãŒå ã®ã©ããã§çºçããJavaScriptãšã©ãŒããã£ããããReactã³ã³ããŒãã³ãã§ããã¢ããªã±ãŒã·ã§ã³å šäœãã¯ã©ãã·ã¥ããã代ããã«ããšã©ãŒããŠã³ããªãŒã¯ãã©ãŒã«ããã¯UIãã¬ã³ããªã³ã°ãããŠãŒã¶ãŒãã¢ããªã±ãŒã·ã§ã³ã䜿ãç¶ããããããã«ããŸãããšã©ãŒããŠã³ããªãŒã¯ãå埩åããããŠãŒã¶ãŒãã¬ã³ããªãŒãªã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããã®éèŠãªããŒã«ã§ãã
ãšã©ãŒããŠã³ããªãŒã®äœæ
ãšã©ãŒããŠã³ããªãŒãäœæããã«ã¯ã`getDerivedStateFromError()`ãŸãã¯`componentDidCatch()`ã©ã€ããµã€ã¯ã«ã¡ãœããïŒãŸãã¯ãã®äž¡æ¹ïŒãæã€ã³ã³ããŒãã³ããå®çŸ©ããå¿ èŠããããŸãããããã®ã¡ãœããã«ããããšã©ãŒããŠã³ããªãŒã¯æ¬¡ã®ããšãè¡ããŸãïŒ
- ãšã©ãŒããã°ã«èšé²ããã
- ãã©ãŒã«ããã¯UIã衚瀺ããã
- ã¢ããªã±ãŒã·ã§ã³ã®ã¯ã©ãã·ã¥ãé²ãã
äŸïŒãšã©ãŒããŠã³ããªãŒã®å®è£
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.error('Caught error:', error, errorInfo);
// Example using a hypothetical error logging service:
// logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return Something went wrong.
;
}
return this.props.children;
}
}
export default ErrorBoundary;
ãã®äŸã§ã¯ïŒ
- `ErrorBoundary`ã³ã³ããŒãã³ãããã®åã³ã³ããŒãã³ããã©ããããŸãã
- `getDerivedStateFromError`ã¯ãåå«ã³ã³ããŒãã³ãã«ãã£ãŠãšã©ãŒãã¹ããŒãããåŸã«åŒã³åºãããŸããããã¯`hasError`ã¹ããŒããæŽæ°ããŸãã
- `componentDidCatch`ã¯ããšã©ãŒãã¹ããŒãããåŸã«åŒã³åºãããŸããããã«ãããšã©ãŒããã°ã«èšé²ã§ããŸãã
- `hasError`ãtrueã®å Žåããã©ãŒã«ããã¯UIïŒäŸïŒãSomething went wrong.ãïŒãã¬ã³ããªã³ã°ãããŸãããã以å€ã®å Žåã¯ãåã³ã³ããŒãã³ããã¬ã³ããªã³ã°ãããŸãã
Suspenseãšãšã©ãŒããŠã³ããªãŒã®äœ¿çš
ãšã©ãŒããŠã³ããªãŒãšSuspenseã¯ããŸã飿ºããŸããäžæãããã³ã³ããŒãã³ãå ã§ãšã©ãŒãçºçããå Žåããšã©ãŒããŠã³ããªãŒãããããã£ããããŸããããã«ãããããŒã¿ãã§ãããã³ã³ããŒãã³ãã®ã¬ã³ããªã³ã°ã«åé¡ããã£ãŠããã¢ããªã±ãŒã·ã§ã³ãã¯ã©ãã·ã¥ããªãããšãä¿èšŒãããŸããäžæãããã³ã³ããŒãã³ãã®åšãã«æŠç¥çã«ãšã©ãŒããŠã³ããªãŒããã¹ãããããšã§ãäºæãã¬ãšã©ãŒã«å¯Ÿããä¿è·å±€ãæäŸããŸãã
äŸïŒãšã©ãŒããŠã³ããªãŒãšSuspenseã®çµã¿åãã
import React, { Suspense } from 'react';
import ErrorBoundary from './ErrorBoundary'; // Assuming ErrorBoundary from the previous example
const ProfileDetails = React.lazy(() => import('./ProfileDetails')); // Assume this is the ProfileDetails component from earlier
function App() {
return (
My App
Loading profile... }>